package com.example.matchandroiddevices.test.rfid;

import android.os.Handler;
import android.os.Looper;
import android.util.Log;

import com.example.matchandroiddevices.test.UsbSerialCommunicator;

import java.util.HashMap;
import java.util.Map;

/**
 * ISO15693 RFID读卡器通信类
 * 根据提供的API文档实现
 */
public class ISO15693CardReader implements UsbSerialCommunicator.SerialDataListener {
    private static final String TAG = "ISO15693Reader";

    // 串口通信对象
    private UsbSerialCommunicator serialComm;
    // 结果回调
    private CardReaderCallback callback;
    // 主线程Handler用于回调
    private Handler mainHandler;
    // 扫描结果缓存
    private Map<String, Integer> scanResults = new HashMap<>();

    // 命令响应处理
    private ResponseHandler currentResponseHandler;
    private byte[] receivedData = new byte[0];

    // 标签信息缓存
    private byte[] lastUID = null;

    // 命令格式常量
    private static final byte CMD_PREFIX = (byte)0xAA;  // 命令前缀
    private static final byte CMD_SUFFIX = (byte)0xBB;  // 命令后缀

    // 命令代码
    private static final byte CMD_CONNECT = 0x01;      // 连接读卡器
    private static final byte CMD_DISCONNECT = 0x02;   // 断开读卡器
    private static final byte CMD_OPEN_RF = 0x03;      // 打开射频
    private static final byte CMD_CLOSE_RF = 0x04;     // 关闭射频
    private static final byte CMD_SET_ANTENNA = 0x05;  // 设置天线
    private static final byte CMD_GET_INFO = 0x06;     // 获取读卡器信息
    private static final byte CMD_INVENTORY = 0x07;    // 询查标签
    private static final byte CMD_READ_BLOCK = 0x08;   // 读单个块
    private static final byte CMD_WRITE_BLOCK = 0x09;  // 写单个块
    private static final byte CMD_READ_MULTI = 0x0A;   // 读多个块

    /**
     * 读卡器回调接口
     */
    public interface CardReaderCallback {
        void onVersionInfo(String version);
        void onCardsDetected(Map<String, Integer> cards);
        void onReadData(String cardId, byte[] data);
        void onWriteResult(String cardId, boolean success);
        void onError(String message);
    }

    /**
     * 构造函数
     */
    public ISO15693CardReader(UsbSerialCommunicator serialComm, CardReaderCallback callback) {
        this.serialComm = serialComm;
        this.callback = callback;
        this.mainHandler = new Handler(Looper.getMainLooper());
    }

    /**
     * 打开读卡器
     */
    public boolean openReader() {
        if (serialComm == null) {
            notifyError("串口通信对象未初始化");
            return false;
        }

        // 根据文档提供的API，首先需要连接读卡器
        return connectReader();
    }

    /**
     * 连接读卡器，对应文档的ConnectReader
     */
    private boolean connectReader() {
        // 由于我们使用串口直接通信，这里只需打开射频
        boolean success = openRF();
        if (success) {
            // 获取读卡器信息
            getReaderInfo();
        }
        return success;
    }

    /**
     * 获取读卡器版本信息，对应文档的GetReaderInfo
     */
    public void getReaderInfo() {
        Log.d(TAG, "获取读卡器信息");

        // 准备命令
        // 根据文档：GetReaderInfo(byte VersionInfo[], byte RFU[], byte ReaderType[], byte TrType[], byte InventoryScanTime[])
        // 我们使用串口方式实现，构造命令格式
        byte[] cmd = new byte[] {
                CMD_PREFIX,           // 命令前缀
                CMD_GET_INFO,         // 命令代码
                0x00,                 // 数据长度
                calculateChecksum(new byte[]{CMD_GET_INFO, 0x00}),  // 校验和
                CMD_SUFFIX            // 命令后缀
        };

        currentResponseHandler = new ResponseHandler() {
            @Override
            public void handleResponse(byte[] response) {
                if (isValidResponse(response, CMD_GET_INFO)) {
                    // 按文档解析：返回的VersionInfo是2字节
                    if (response.length >= 7) { // 至少包含前缀+命令+长度+状态+2字节版本+校验和+后缀
                        int dataStartPos = 4; // 跳过前缀、命令、长度和状态
                        byte[] versionInfo = new byte[2];
                        System.arraycopy(response, dataStartPos, versionInfo, 0, 2);

                        String version = String.format("%d.%d", versionInfo[0] & 0xFF, versionInfo[1] & 0xFF);
                        notifyVersionInfo(version);
                        Log.d(TAG, "读卡器版本: " + version);
                    } else {
                        notifyError("版本信息响应格式错误");
                    }
                } else {
                    notifyError("获取版本信息失败");
                }
            }
        };

        sendCommand(cmd);
    }

    /**
     * 打开射频，对应文档的OpenRf
     */
    public boolean openRF() {
        Log.d(TAG, "打开射频");

        // 准备命令
        byte[] cmd = new byte[] {
                CMD_PREFIX,           // 命令前缀
                CMD_OPEN_RF,          // 命令代码
                0x00,                 // 数据长度
                calculateChecksum(new byte[]{CMD_OPEN_RF, 0x00}),  // 校验和
                CMD_SUFFIX            // 命令后缀
        };

        // 发送命令
        sendCommand(cmd);

        // 由于我们使用异步方式，这里无法直接返回结果
        // 假设成功打开，实际结果在回调中处理
        return true;
    }

    /**
     * 关闭射频，对应文档的CloseRf
     */
    public void closeRF() {
        Log.d(TAG, "关闭射频");

        // 准备命令
        byte[] cmd = new byte[] {
                CMD_PREFIX,           // 命令前缀
                CMD_CLOSE_RF,         // 命令代码
                0x00,                 // 数据长度
                calculateChecksum(new byte[]{CMD_CLOSE_RF, 0x00}),  // 校验和
                CMD_SUFFIX            // 命令后缀
        };

        // 发送命令
        sendCommand(cmd);
    }

    /**
     * 设置天线，对应文档的SetActiveANT
     */
    public void setAntenna(byte antenna) {
        Log.d(TAG, "设置天线: " + antenna);

        // 转换成文档要求的格式：1-天线1；2-天线2；4-天线3；8-天线4
        byte antVal;
        switch(antenna) {
            case 1: antVal = 1; break;
            case 2: antVal = 2; break;
            case 3: antVal = 4; break;
            case 4: antVal = 8; break;
            default: antVal = 1; break;
        }

        // 准备命令
        byte[] data = new byte[] { antVal };
        byte[] cmd = new byte[] {
                CMD_PREFIX,           // 命令前缀
                CMD_SET_ANTENNA,      // 命令代码
                0x01,                 // 数据长度
                antVal,               // 数据
                calculateChecksum(new byte[]{CMD_SET_ANTENNA, 0x01, antVal}),  // 校验和
                CMD_SUFFIX            // 命令后缀
        };

        // 发送命令
        sendCommand(cmd);
    }

    /**
     * 扫描卡片，对应文档的Inventory
     */
    public void scanCards() {
        Log.d(TAG, "扫描卡片");

        // 根据文档：Inventory(byte state, byte Data[], int cardnum[])
        // state=0x06表示"不带AFI的InventoryScan: 0x06;（新的询查）"
        byte state = 0x06;

        // 准备命令
        byte[] data = new byte[] { state };
        byte[] cmd = new byte[] {
                CMD_PREFIX,           // 命令前缀
                CMD_INVENTORY,        // 命令代码
                0x01,                 // 数据长度
                state,                // 状态值
                calculateChecksum(new byte[]{CMD_INVENTORY, 0x01, state}),  // 校验和
                CMD_SUFFIX            // 命令后缀
        };

        currentResponseHandler = new ResponseHandler() {
            @Override
            public void handleResponse(byte[] response) {
                if (!isValidResponse(response, CMD_INVENTORY)) {
                    notifyError("扫描响应格式错误");
                    return;
                }

                // 解析响应
                int status = response[3] & 0xFF; // 状态码

                if (status == 0x0e) {
                    // 命令执行结束，无标签或全部标签解析完成
                    if (response.length <= 6) { // 前缀+命令+长度+状态+校验和+后缀，无卡数据
                        notifyCardsDetected(new HashMap<>());
                        return;
                    }
                } else if (status == 0x0a || status == 0x0b) {
                    // 部分解析或时间已到
                    Log.d(TAG, "标签解析未完全，状态码: " + status);
                }

                // 提取数据部分
                int dataLength = response[2] & 0xFF;
                if (dataLength < 2) { // 至少有卡数量信息(1字节)和状态码(1字节)
                    notifyCardsDetected(new HashMap<>());
                    return;
                }

                int cardCount = response[4] & 0xFF; // 卡数量在状态码后面
                Log.d(TAG, "检测到 " + cardCount + " 张卡");

                if (cardCount == 0) {
                    notifyCardsDetected(new HashMap<>());
                    return;
                }

                // 每张卡的数据是9字节（8字节UID + 1字节DSFID）
                int dataStartPos = 5; // 跳过前缀、命令、长度、状态和卡数量
                Map<String, Integer> cards = new HashMap<>();

                for (int i = 0; i < cardCount && dataStartPos + 9 <= response.length - 2; i++) {
                    byte[] uid = new byte[8];
                    System.arraycopy(response, dataStartPos, uid, 0, 8);

                    String cardId = bytesToHex(uid);
                    Integer count = scanResults.get(cardId);
                    if (count == null) count = 0;
                    cards.put(cardId, count + 1);

                    // 保存最近一次扫描到的UID，方便后续读写操作
                    lastUID = uid;

                    dataStartPos += 9; // 移到下一张卡
                }

                scanResults = cards;
                notifyCardsDetected(scanResults);
            }
        };

        // 发送命令
        sendCommand(cmd);
    }

    /**
     * 读取数据块，对应文档的ReadSingleBlock
     */
    public void readBlock(String cardId, byte blockNumber) {
        Log.d(TAG, "读取块数据，卡ID: " + cardId + ", 块号: " + blockNumber);

        byte[] uid;
        if (cardId != null && !cardId.isEmpty()) {
            uid = hexToBytes(cardId);
        } else if (lastUID != null) {
            uid = lastUID;
            cardId = bytesToHex(lastUID);
        } else {
            notifyError("没有有效的卡ID");
            return;
        }

        if (uid.length != 8) {
            notifyError("卡ID必须是8字节");
            return;
        }

        // 根据文档: ReadSingleBlock(byte state, byte UID[], byte blockNumber, byte SecurityWord[], byte Data[], byte ErrorCode[])
        // state=0x00表示"块的字节数为4时的情况,Addressed模式：0x00"
        byte state = 0x00;

        // 准备命令
        // 数据为: 状态(1字节) + UID(8字节) + 块号(1字节)
        byte[] data = new byte[10];
        data[0] = state;
        System.arraycopy(uid, 0, data, 1, 8);
        data[9] = blockNumber;

        byte dataLen = (byte)data.length;

        // 构造完整命令
        byte[] cmd = new byte[5 + data.length];
        cmd[0] = CMD_PREFIX;
        cmd[1] = CMD_READ_BLOCK;
        cmd[2] = dataLen;
        System.arraycopy(data, 0, cmd, 3, data.length);

        // 计算校验和 - 命令码 + 长度 + 所有数据
        byte[] checkSumData = new byte[1 + 1 + data.length];
        checkSumData[0] = CMD_READ_BLOCK;
        checkSumData[1] = dataLen;
        System.arraycopy(data, 0, checkSumData, 2, data.length);
        cmd[3 + data.length] = calculateChecksum(checkSumData);

        cmd[4 + data.length] = CMD_SUFFIX;

        final String finalCardId = cardId;
        currentResponseHandler = new ResponseHandler() {
            @Override
            public void handleResponse(byte[] response) {
                if (!isValidResponse(response, CMD_READ_BLOCK)) {
                    notifyError("读取响应格式错误");
                    return;
                }

                // 解析响应
                int status = response[3] & 0xFF; // 状态码
                if (status != 0x00) {
                    notifyError("读取失败，错误码: " + status);
                    return;
                }

                // 提取数据部分 - 4字节块数据
                int dataStartPos = 4; // 跳过前缀、命令、长度和状态
                if (dataStartPos + 4 <= response.length - 2) { // 确保有足够的数据字节(不包括校验和和后缀)
                    byte[] blockData = new byte[4];
                    System.arraycopy(response, dataStartPos, blockData, 0, 4);
                    notifyReadData(finalCardId, blockData);
                } else {
                    notifyError("读取响应数据不完整");
                }
            }
        };

        // 发送命令
        sendCommand(cmd);
    }

    /**
     * 写数据块，对应文档的WriteSingleBlock
     */
    public void writeBlock(String cardId, byte blockNumber, byte[] data) {
        if (data.length != 4) {
            notifyError("数据必须是4字节");
            return;
        }

        Log.d(TAG, "写入块数据，卡ID: " + cardId + ", 块号: " + blockNumber);

        byte[] uid;
        if (cardId != null && !cardId.isEmpty()) {
            uid = hexToBytes(cardId);
        } else if (lastUID != null) {
            uid = lastUID;
            cardId = bytesToHex(lastUID);
        } else {
            notifyError("没有有效的卡ID");
            return;
        }

        if (uid.length != 8) {
            notifyError("卡ID必须是8字节");
            return;
        }

        // 根据文档: WriteSingleBlock(byte state, byte UID[], byte blockNumber, byte Data[], byte ErrorCode[])
        // state=0x00表示"块的字节数为4的情况,Addressed模式：0x00（A类标签）"
        byte state = 0x00;

        // 准备命令
        // 数据为: 状态(1字节) + UID(8字节) + 块号(1字节) + 写入数据(4字节)
        byte[] cmdData = new byte[14];
        cmdData[0] = state;
        System.arraycopy(uid, 0, cmdData, 1, 8);
        cmdData[9] = blockNumber;
        System.arraycopy(data, 0, cmdData, 10, 4);

        byte dataLen = (byte)cmdData.length;

        // 构造完整命令
        byte[] cmd = new byte[5 + cmdData.length];
        cmd[0] = CMD_PREFIX;
        cmd[1] = CMD_WRITE_BLOCK;
        cmd[2] = dataLen;
        System.arraycopy(cmdData, 0, cmd, 3, cmdData.length);

        // 计算校验和 - 命令码 + 长度 + 所有数据
        byte[] checkSumData = new byte[1 + 1 + cmdData.length];
        checkSumData[0] = CMD_WRITE_BLOCK;
        checkSumData[1] = dataLen;
        System.arraycopy(cmdData, 0, checkSumData, 2, cmdData.length);
        cmd[3 + cmdData.length] = calculateChecksum(checkSumData);

        cmd[4 + cmdData.length] = CMD_SUFFIX;

        final String finalCardId = cardId;
        currentResponseHandler = new ResponseHandler() {
            @Override
            public void handleResponse(byte[] response) {
                if (!isValidResponse(response, CMD_WRITE_BLOCK)) {
                    notifyError("写入响应格式错误");
                    return;
                }

                // 解析响应
                int status = response[3] & 0xFF; // 状态码
                boolean success = (status == 0x00);

                if (success) {
                    notifyWriteResult(finalCardId, true);
                } else {
                    notifyError("写入失败，错误码: " + status);
                    notifyWriteResult(finalCardId, false);
                }
            }
        };

        // 发送命令
        sendCommand(cmd);
    }

    /**
     * 读取多个数据块，对应文档的ReadMultipleBlock
     */
    public void readMultipleBlocks(String cardId, byte startBlock, byte blockCount) {
        Log.d(TAG, "读取多个块数据，卡ID: " + cardId + ", 起始块: " + startBlock + ", 块数: " + blockCount);

        byte[] uid;
        if (cardId != null && !cardId.isEmpty()) {
            uid = hexToBytes(cardId);
        } else if (lastUID != null) {
            uid = lastUID;
            cardId = bytesToHex(lastUID);
        } else {
            notifyError("没有有效的卡ID");
            return;
        }

        if (uid.length != 8) {
            notifyError("卡ID必须是8字节");
            return;
        }

        // 根据文档: ReadMultipleBlock(byte state, byte UID[], byte startblockNumber, byte numberOfBlock, byte SecurityWord[], byte Data[], byte ErrorCode[])
        // state=0x00表示"块的字节数为4时的情况,Addressed模式：0x00"
        byte state = 0x00;

        // 准备命令
        // 数据为: 状态(1字节) + UID(8字节) + 起始块号(1字节) + 块数(1字节)
        byte[] data = new byte[11];
        data[0] = state;
        System.arraycopy(uid, 0, data, 1, 8);
        data[9] = startBlock;
        data[10] = blockCount;

        byte dataLen = (byte)data.length;

        // 构造完整命令
        byte[] cmd = new byte[5 + data.length];
        cmd[0] = CMD_PREFIX;
        cmd[1] = CMD_READ_MULTI;
        cmd[2] = dataLen;
        System.arraycopy(data, 0, cmd, 3, data.length);

        // 计算校验和 - 命令码 + 长度 + 所有数据
        byte[] checkSumData = new byte[1 + 1 + data.length];
        checkSumData[0] = CMD_READ_MULTI;
        checkSumData[1] = dataLen;
        System.arraycopy(data, 0, checkSumData, 2, data.length);
        cmd[3 + data.length] = calculateChecksum(checkSumData);

        cmd[4 + data.length] = CMD_SUFFIX;

        // 未实现多块读取的回调处理，如有需要可以添加

        // 发送命令
        sendCommand(cmd);
    }

    /**
     * 发送命令到读卡器
     */
    private void sendCommand(byte[] command) {
        if (serialComm != null) {
            Log.d(TAG, "发送命令: " + bytesToHex(command));
            serialComm.sendData(command);
        } else {
            Log.e(TAG, "串口未连接");
            notifyError("串口未连接");
        }
    }

    /**
     * 校验响应是否有效
     */
    private boolean isValidResponse(byte[] response, byte expectedCmd) {
        if (response == null || response.length < 5) { // 最小长度：前缀+命令+长度+状态+校验和+后缀
            Log.e(TAG, "响应无效: 长度不足");
            return false;
        }

        if (response[0] != CMD_PREFIX || response[response.length - 1] != CMD_SUFFIX) {
            Log.e(TAG, "响应无效: 前缀或后缀错误");
            return false;
        }

        if (response[1] != expectedCmd) {
            Log.e(TAG, "响应无效: 命令不匹配，期望" + expectedCmd + "，实际" + response[1]);
            return false;
        }

        // TODO: 可以添加校验和验证

        return true;
    }

    /**
     * 计算校验和 (XOR所有字节)
     */
    private byte calculateChecksum(byte[] data) {
        byte checksum = 0;
        for (byte b : data) {
            checksum ^= b;
        }
        return checksum;
    }

    /**
     * 响应处理接口
     */
    private interface ResponseHandler {
        void handleResponse(byte[] response);
    }

    /**
     * SerialDataListener 接口实现
     * 处理接收到的数据
     */
    @Override
    public void onDataReceived(byte[] data) {
        Log.d(TAG, "收到数据: " + bytesToHex(data));

        // 合并接收到的数据
        byte[] newData = new byte[receivedData.length + data.length];
        System.arraycopy(receivedData, 0, newData, 0, receivedData.length);
        System.arraycopy(data, 0, newData, receivedData.length, data.length);
        receivedData = newData;

        // 检查是否有完整的响应包
        if (receivedData.length >= 5) { // 最小长度：前缀+命令+长度+状态+校验和+后缀
            // 查找前缀
            int startIdx = -1;
            for (int i = 0; i < receivedData.length - 4; i++) {
                if (receivedData[i] == CMD_PREFIX) {
                    startIdx = i;
                    break;
                }
            }

            if (startIdx >= 0) {
                // 查找后缀
                int endIdx = -1;
                for (int i = startIdx + 4; i < receivedData.length; i++) {
                    if (receivedData[i] == CMD_SUFFIX) {
                        endIdx = i;
                        break;
                    }
                }

                if (endIdx > startIdx) {
                    // 提取完整的响应包
                    byte[] responsePacket = new byte[endIdx - startIdx + 1];
                    System.arraycopy(receivedData, startIdx, responsePacket, 0, responsePacket.length);

                    // 处理响应
                    if (currentResponseHandler != null) {
                        currentResponseHandler.handleResponse(responsePacket);
                        currentResponseHandler = null;
                    }

                    // 移除已处理的数据
                    if (endIdx < receivedData.length - 1) {
                        byte[] remainingData = new byte[receivedData.length - endIdx - 1];
                        System.arraycopy(receivedData, endIdx + 1, remainingData, 0, remainingData.length);
                        receivedData = remainingData;
                    } else {
                        receivedData = new byte[0];
                    }
                }
            }
        }
    }

    @Override
    public void onError(Exception e) {
        Log.e(TAG, "串口通信错误", e);
        notifyError("串口通信错误: " + e.getMessage());
    }

    /**
     * 通知版本信息
     */
    private void notifyVersionInfo(final String version) {
        mainHandler.post(new Runnable() {
            @Override
            public void run() {
                if (callback != null) {
                    callback.onVersionInfo(version);
                }
            }
        });
    }

    /**
     * 通知卡片检测结果
     */
    private void notifyCardsDetected(final Map<String, Integer> cards) {
        mainHandler.post(new Runnable() {
            @Override
            public void run() {
                if (callback != null) {
                    callback.onCardsDetected(cards);
                }
            }
        });
    }

    /**
     * 通知读取数据结果
     */
    private void notifyReadData(final String cardId, final byte[] data) {
        mainHandler.post(new Runnable() {
            @Override
            public void run() {
                if (callback != null) {
                    callback.onReadData(cardId, data);
                }
            }
        });
    }

    /**
     * 通知写入结果
     */
    private void notifyWriteResult(final String cardId, final boolean success) {
        mainHandler.post(new Runnable() {
            @Override
            public void run() {
                if (callback != null) {
                    callback.onWriteResult(cardId, success);
                }
            }
        });
    }

    /**
     * 通知错误
     */
    private void notifyError(final String message) {
        mainHandler.post(new Runnable() {
            @Override
            public void run() {
                if (callback != null) {
                    callback.onError(message);
                }
            }
        });
    }

    /**
     * 字节数组转十六进制字符串
     */
    public static String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02X", b & 0xff));
        }
        return sb.toString();
    }

    /**
     * 十六进制字符串转字节数组
     */
    public static byte[] hexToBytes(String hexString) {
        if (hexString == null || hexString.equals("")) {
            return null;
        }
        hexString = hexString.toUpperCase();
        int length = hexString.length() / 2;
        char[] hexChars = hexString.toCharArray();
        byte[] d = new byte[length];
        for (int i = 0; i < length; i++) {
            int pos = i * 2;
            d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
        }
        return d;
    }

    private static byte charToByte(char c) {
        return (byte) "0123456789ABCDEF".indexOf(c);
    }

    /**
     * 关闭读卡器
     */
    public void closeReader() {
        closeRF();

        // 断开连接
        byte[] cmd = new byte[] {
                CMD_PREFIX,           // 命令前缀
                CMD_DISCONNECT,       // 命令代码
                0x00,                 // 数据长度
                calculateChecksum(new byte[]{CMD_DISCONNECT, 0x00}),  // 校验和
                CMD_SUFFIX            // 命令后缀
        };

        sendCommand(cmd);

        // 清理资源
        receivedData = new byte[0];
        currentResponseHandler = null;
        scanResults.clear();
        lastUID = null;
    }
}